//
//  SQLiteManager.swift
//  SqlDemo
//
//  Created by 好米有易 on 2018/8/20.
//  Copyright © 2018年 好米有易. All rights reserved.
//

import Foundation

// SQLite 框架是纯C语言的框架
// 所有的函数都是以 sqlite3_ 开始的
class SQLiteManager {

    // 单例
    static let shareManager = SQLiteManager()

    /// 全局数据库操作句柄
    fileprivate var db: OpaquePointer? = nil

    /// 打开数据库
    ///
    /// - Parameter dbName: 数据名称
    func openDB(dbName: String) {

        let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last! + "/" + dbName
        print(path)

        // 数据库的‘全路径’ Int8 -> unit8 -> Byte / char c 语言字符串  Xcode& beta 5 开始可以使用string传递
        // 全局数据库访问 ‘句柄’ -> 指针 后续对所有的数据库操作 全是基于此句柄
        // 返回值 如果 == SQLITE_OK 表示成功
        // 数据库不存在 创建之后再打开 数据库存在直接打开
        if sqlite3_open(path, &db) != SQLITE_OK {

            print("打开数据库失败")
            return
        }

        print("打开数据库成功")
        if createTab()  {

            print("创建数据库成")
        }else {

            print("创建数据失败")
        }
    }


    func execRecordSet(sql: String) -> [[String:Any]]? {

        var stmt: OpaquePointer? = nil

//     参数1、全局句柄
//     参数2、要执行的SQL c语言字符串
//     参数3、要执行的SQL的以字节为单位的长度 如果传入-1 SQLite或自动计算
//     参数4、stmt 预编译指令的句柄
//           后续针对本次查询的所有操作都是基于此句柄
//           注意句柄一定要释放
//           编译完成后 可以理解为一个临时的数据集合 通过 step 函数 能够顺序获取其中的结果
//     参数5、通常传入nil
//        返回值 编译成功标识 Sql 能够正常执行 返回 SQLITE_OK
        if sqlite3_prepare(db, sql, -1, &stmt, nil) != SQLITE_OK {

            print("Sql 错误")
            sqlite3_finalize(stmt)
            return nil
        }

        var result = [[String:Any]]()

//    单步执行获得结果 row -- 对应一条完好着呢的记录
        while sqlite3_step(stmt) == SQLITE_ROW {

           result.append(recorod(stmt: stmt!))
        }

         print("Sql 正确")
         sqlite3_finalize(stmt)

        return result

    }

   /// 获取当前记录的内容
   ///
   /// - Parameter stmt: 句柄
   fileprivate func recorod(stmt: OpaquePointer) -> [String: Any]{

     //1、 查询列数
       let columns = sqlite3_column_count(stmt)

    var row = [String: Any]()

    //2、循环每一列 获取每一列的内容
    for column in 0..<columns {

        /// 列名
        let cName = sqlite3_column_name(stmt, column)

        let name = String(cString: cName!, encoding: String.Encoding.utf8)

        /// 数据类型
        let dataType = sqlite3_column_type(stmt, column)

        var value: Any?

        switch dataType {

        case SQLITE_INTEGER:

              value = sqlite3_column_int(stmt, column)
            
        case SQLITE3_TEXT:

             let cText = sqlite3_column_text(stmt, column)

             value = String(cString: cText!)
       case SQLITE_NULL:

             value = NSNull()
        default:
             print("不支持类型")
        }

        print(name!)
        row[name!] = value
    }

       print(columns)

        return row
    }


    /// 执行SQl 更新数据 删除数据
    ///
    /// - Parameter sql: sql
    /// - Returns: 返回影响的行数
    func execUpdate(sql: String) -> Int {

        if !execSQL(sql: sql) {

            // 指定失败返回 -1
            return -1
        }

        return Int(sqlite3_changes(db))
    }

    /// 执行SQl 插入数据
    ///
    /// - Parameter sql: sql
    /// - Returns: 返回自动增城的id
    func execInsert(sql: String) -> Int {

        if !execSQL(sql: sql) {

            // 指定失败返回 -1
            return -1
        }

        return Int(sqlite3_last_insert_rowid(db))
    }

    /// 执行sql命令
    ///
    /// - Parameter sql: sql
    /// - Returns: 返回是否正确
    func execSQL(sql: String) -> Bool {
        // 参数 1、数据库句柄
        // 参数 2、要执行的SQL
        // 参数 3、callback 执行完成 Sql 之后 调用的 c 语言函数指针 通常传入 nil
        // 参数 4、第三个参数 callback， 函数的地址 通常传入 nil
        // 参数 5、错误信息 有其他方式获取执行情况 通常传入nil

        print("sqlite3_exec")
        return  sqlite3_exec(db, sql, nil, nil, nil) == SQLITE_OK

    }

    /// 创建数据表 拼接字符串末尾加 \n 可以避免拼接字符时 造成字符串连接错误
    ///
    /// - Returns: 是否成功
    fileprivate func createTab() -> Bool {
       let sql = "CREATE TABLE IF NOT EXISTS 'T_Person' (\n" +
            "'id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n" +
            "'name' TEXT \n" +
       ");"
        return execSQL(sql: sql)
    }


   
}
